/*
 * $AIST_Release: 0.9.0 $
 * Copyright 2011 Information Technology Research Institute, National
 * Institute of Advanced Industrial Science and Technology
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *    http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package generator;

import java.util.Map;
import java.util.HashMap;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.io.Serializable;
import java.io.ObjectOutputStream;
import java.io.ObjectInputStream;
import java.io.IOException;
import com.google.protobuf.ByteString;
import aggr.SerializedTypes;

/**
 * Helper module.
 */
public class Helpers {

    /**
     * Base interface the generated static variable class.
     */
    public static interface StaticVariables {
    }

    /**
     * Base interface the generated table arguments class.
     */
    public static interface TableArguments {
      public Map<Integer, Object[]> getValues();
    }

    /**
     * Key/Value pair.
     */
    public static class KeyValue<K, V> {
        public K key;
        public V value;

        public KeyValue(K k, V v) {
            key   = k;
            value = v;
        }
    }

    /**
     * Makes map.
     */
    public static <K, V> Map<K, V> makeMap(KeyValue<K, V>... kv) {
        Map<K, V> m = new HashMap<K, V>();

        for (KeyValue<K, V> item: kv) {
            m.put(item.key, item.value);
        }
        return m;
    }

    /**
     * 'time' data type.
     */
    public static class Time implements Serializable {
        public Time() {
          value = 0;
        }
        public Time(long v) {
          value = v;
        }

        public String toString() {
          return "Time(" + value + ")";
        }

        public long value;
    }

    /**
     * 'fingerprint' data type.
     */
    public static class FingerPrint implements Serializable {
        public FingerPrint() {
          value = 0;
        }
        public FingerPrint(long v) {
          value = v;
        }

        public String toString() {
          return "FingerPrint(" + value + ")";
        }

        public long value;
    }

    /**
     * 'bytes' data type.
     *
     * This wraps 'ByteString'. Because ByteString is not Serializable.
     */
    public static class ByteStringWrapper implements Serializable {
        private transient ByteString value;

        public ByteStringWrapper(ByteString v) {
            value = v;
        }

        public ByteStringWrapper() {
            value = ByteString.EMPTY;
        }

        public ByteString get() {
            return value;
        }
        public Long byteAt(int i) {
            return new Long(value.byteAt(i));
        }

        private void writeObject(ObjectOutputStream stream) throws IOException {
            stream.defaultWriteObject();

            stream.writeInt(value.size());
            stream.write(value.toByteArray());
        }

        private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException {
            stream.defaultReadObject();

            int size = stream.readInt();
            byte[] b = new byte[size];

            stream.read(b, 0, b.length);

            value = ByteString.copyFrom(b);
        }
    }

    /**
     * Empty ByteStringWrapper.
     */
    static public ByteStringWrapper EMPTY_BYTESTRING = new ByteStringWrapper(ByteString.EMPTY);

    /**
     * Class for local variable referred in closure.
     */
    static public class Enclosing<T> {
        static Long ret = new Long(1l);
        private T value = null;

        public Enclosing(T v) {
            this.value = v;
        }

        public T get() {
            return value;
        }

        // Return type
        public Long assign(T v) {
            this.value = v;
            return ret;
        }
    }

    /**
     * Iterator of index..
     *
     * It returns 0 to 'until - 1'.
     */
    public static class IndexIterator implements Iterator<Long> {
        private long cur   = 0l;
        private long until;

        public IndexIterator(int until) {
            this.until = until;
        }

        public boolean hasNext() {
            return cur < until;
        }

        public Long next() throws NoSuchElementException {
            if (!hasNext()) {
                throw new NoSuchElementException();
            }
            try {
                return cur;
            } finally {
                cur++;
            }
        }

        public void remove() {
            throw new java.lang.UnsupportedOperationException();
        }
    }

    /**
     * Table information used in generated code.
     */
    public static class Table {
        public final int id;
        public final SerializedTypes.SerializedType keyType;
        public final SerializedTypes.SerializedType valueType;

        public Table(int id, SerializedTypes.SerializedType keyType, SerializedTypes.SerializedType valueType) {
            this.id        = id;
            this.keyType   = keyType;
            this.valueType = valueType;
        }
    }

    /**
     * Base interface of the generated mapper.
     */
    public static interface Mapper {
        public void map(Emitter emitter, ByteStringWrapper input) throws java.lang.Throwable;
    }
}
